package org.hepeng.workx.mybatis.builder.xml;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.session.Configuration;
import org.hepeng.workx.extension.XLoader;
import org.hepeng.workx.mybatis.MybatisConstant;
import org.joor.Reflect;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author he peng
 */
public class XMLConfigBuilderX extends XMLConfigBuilder {

    private static final XLoader<Configuration> X_LOADER = XLoader.getXLoader(Configuration.class, MybatisConstant.MYBATIS_X_POINT_DIRECTORY);
    private static final Map<String , Object> OBJECT_MAP = new ConcurrentHashMap<>();

    public XMLConfigBuilderX(Reader reader) {
        this(reader , null);
    }

    public XMLConfigBuilderX(Reader reader, String environment) {
        this(reader, environment , null);
    }

    public XMLConfigBuilderX(InputStream inputStream) {
        this(inputStream , null);
    }

    public XMLConfigBuilderX(InputStream inputStream, String environment) {
        this(inputStream, environment , null);
    }

    public XMLConfigBuilderX(InputStream inputStream, String environment, Properties props) {
        this(new InputStreamReader(inputStream) , environment, props);
    }

    public XMLConfigBuilderX(Reader reader, String environment, Properties props) {
        super(copyReader(reader) , environment , props);

        Configuration configuration = X_LOADER.getX();
        Reflect thisReflect = Reflect.on(this);
        thisReflect.set("configuration" , configuration);
        thisReflect.set("typeAliasRegistry" , configuration.getTypeAliasRegistry());
        thisReflect.set("typeHandlerRegistry" , configuration.getTypeHandlerRegistry());
        ErrorContext.instance().resource("SQL Mapper Configuration");
        super.configuration.setVariables(props);
        thisReflect.set("parsed" , false);
        thisReflect.set("environment" , environment);
        thisReflect.set("parser" , new XPathParser((InputStream) OBJECT_MAP.get("inputstream"), true, props, new XMLMapperEntityResolver()));
    }

    private static Reader copyReader(Reader souce) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        InputStream inputStream;
        try {
            try {
                IOUtils.copy(new ReaderInputStream(souce , Charset.forName("UTF-8")) , outputStream);
            } catch (IOException e) {
                throw ExceptionFactory.wrapException("" , e);
            }
            inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            InputStream backupInputStream = new ByteArrayInputStream(outputStream.toByteArray());
            OBJECT_MAP.put("inputstream" , backupInputStream);
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                throw ExceptionFactory.wrapException("" , e);
            }
        }
        return new InputStreamReader(inputStream);
    }
}
